﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SampleLibrary;

namespace MethodCaching.Tests
{
    
    [TestClass]
    public class CachingTests
    {
        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext { get; set; }

        [TestMethod]
        public void CompareFunctionDelegates()
        {
            Func<int,int> f1 = SomeMethod;
            Func<int, int> f2 = SomeMethod;
            Assert.AreEqual(f1,f2);
            Func<int, int> f3 = SomeOtherMethod;
            Assert.AreNotEqual(f1, f3);
        }


        private static int SomeMethod(int input)
        {
            return input;
        }

        private static int SomeOtherMethod(int input)
        {
            return input;
        }

        [TestMethod]
        public void CacheProviderMustInvokeTargetMethodOnlyOnce()
        {
            var mock = new Mock<ISampleClass>(MockBehavior.Strict);
            mock.Setup(t => t.GetValue()).Returns(0);
            mock.Setup(t => t.GetValue(1)).Returns(1);
            mock.Setup(t => t.GetValue(1, 2)).Returns(3);
            mock.Setup(t => t.GetValue(1, 2, 3)).Returns(6);
            mock.Setup(t => t.GetValue(1, 2, 3, 4)).Returns(10);
            var func = CacheProvider.Decorate(() => mock.Object.GetValue());
            var func1 = CacheProvider.Decorate<int, int>(arg => mock.Object.GetValue(arg));
            var func2 = CacheProvider.Decorate<int, int, int>((arg1,arg2) => mock.Object.GetValue(arg1,arg2));
            var func3 = CacheProvider.Decorate<int, int, int, int>((arg1, arg2, arg3) => mock.Object.GetValue(arg1, arg2, arg3));
            var func4 = CacheProvider.Decorate<int, int, int, int, int>((arg1, arg2, arg3, arg4) => mock.Object.GetValue(arg1, arg2, arg3, arg4));
            func();
            func();
            func1(1);
            func1(1);
            func2(1,2);
            func2(1,2);
            func3(1, 2, 3);
            func3(1, 2, 3);
            func4(1, 2, 3, 4);
            func4(1, 2, 3, 4);
            mock.Verify(t => t.GetValue(), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2, 3), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2, 3, 4), Times.AtMostOnce());
        }

        [TestMethod]
        public void MethodCacheMustInvokeTargetMethodOnlyOnce()
        {
            var mock = new Mock<ISampleClass>(MockBehavior.Strict);
            mock.Setup(t => t.GetValue()).Returns(0);
            mock.Setup(t => t.GetValue(1)).Returns(1);
            mock.Setup(t => t.GetValue(1, 2)).Returns(3);
            mock.Setup(t => t.GetValue(1, 2, 3)).Returns(6);
            mock.Setup(t => t.GetValue(1, 2, 3, 4)).Returns(10);
            
            var methodCache = new MethodCache();
            methodCache.Invoke(mock.Object.GetValue);
            methodCache.Invoke(mock.Object.GetValue);
            methodCache.Invoke(mock.Object.GetValue, 1);
            methodCache.Invoke(mock.Object.GetValue, 1);
            methodCache.Invoke(mock.Object.GetValue, 1, 2 );
            methodCache.Invoke(mock.Object.GetValue, 1, 2 );
            methodCache.Invoke(mock.Object.GetValue, 1, 2, 3);
            methodCache.Invoke(mock.Object.GetValue, 1, 2, 3);
            methodCache.Invoke(mock.Object.GetValue, 1, 2, 3, 4);
            methodCache.Invoke(mock.Object.GetValue, 1, 2, 3, 4);
            mock.Verify(t => t.GetValue(), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2, 3), Times.AtMostOnce());
            mock.Verify(t => t.GetValue(1, 2, 3, 4), Times.AtMostOnce());
        }
 

    }
}
